#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright (C) 1994  Ling Li
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.


import gobject, gtk
from gtk import *

import gettext, getopt, locale, logging, logging.handlers, os, sys, thread
from Queue import Queue

from coralftp_globals import *
from local_view import *
from remote_view import *
from transfer_view import *
from log_view import *
from site_manager import *
from configuration import *
from utils import *
import config, filetype

class CoralFTP:

    __site_manager = None
    __clipboard = None

    threads = {}
    ftp_thread_count = 1

    __enable_debug = False

    def __init__(self):
        """Application initialization."""
        # Setup locale & gettext
        locale.setlocale(locale.LC_ALL, '')
        gettext.install('coralftp', coralftp_localedir, unicode=1)
        gtk.glade.bindtextdomain('coralftp', coralftp_localedir)
        gtk.glade.textdomain('coralftp')
        gtk.rc_parse_string('''
        style "about_dialog_program_name" { font_name = "Sans 18" }
        style "main_window_status_bar" { font_name = "Sans 8" }
        widget "about_dialog.*.program_name" style "about_dialog_program_name"
        widget "main_window.*.status_bar.*" style "main_window_status_bar"
        ''')

        # Initialize GNOME
        gtk.threads_init()

        # parse arguments
        try:
            self.opts, self.args = getopt.getopt(sys.argv[1:], 'd', ['debug'])
        except getopt.GetoptError:
            self.usage()
            sys.exit(2)
        for o, a in self.opts:
            if o in ('-d', '--debug'):
                self.__enable_debug = True
        return

    def start(self):
        # create directory USERDATADIR
        if not os.path.exists(USERDATADIR):
            os.mkdir(USERDATADIR)
        if not os.path.isdir(USERDATADIR):
            print "%s should be a directory." % USERDATADIR
            sys.exit(1)

        # choose which kind of config should be used
        if 'gconf' in config.get_available_methods() \
            and not os.path.exists(CONFIG_FILE):
            self.__config = config.get_config('gconf', CONFIG_PREFIX)
        else:
            if not os.path.exists(CONFIG_FILE):
                config.create_standard_config_from_schema_file(
                    CONFIG_FILE,
                    os.path.join(coralftp_schemadir, 'coralftp.schemas'),
                    CONFIG_PREFIX)
            self.__config = config.get_config('standard', CONFIG_FILE)

        # setup filetype method
        method = self.__config.get_string('system', 'filetype_detection')
        methods = filetype.get_available_methods()
        if not method in methods:
            method = methods[0]
            self.__config.set_string('system', 'filetype_detection', method)
        filetype.set_default(method)

        # setup logging
        enable_disk_based_logging = config_value(
            self.__config, 'general', 'enable_disk_based_logging')
        logger = logging.getLogger()
        if enable_disk_based_logging:
            status_log_filename = config_value(
                self.__config, 'general', 'status_log_filename')
            if not status_log_filename or status_log_filename == '':
                status_log_filename = os.path.join(USERDATADIR, 'coralftp.log')
            status_log_filename = os.path.expanduser(status_log_filename)
            trim_status_log_file = config_value(
                self.__config, 'general', 'trim_status_log_file')
            if trim_status_log_file:
                status_log_file_size = config_value(
                    self.__config, 'general', 'status_log_file_size')
                status_log_file_size = status_log_file_size * 1024 * 1024
            else:
                status_log_file_size = 0
            hdlr = logging.handlers.RotatingFileHandler(
                status_log_filename, 'a', status_log_file_size)
        else:
            hdlr = logging.StreamHandler()
        formatter = logging.Formatter(
            '%(asctime)s %(levelname)s %(message)s')
        hdlr.setFormatter(formatter)
        logger.addHandler(hdlr)
        if self.__enable_debug:
            logger.setLevel(logging.DEBUG)
        else:
            logger.setLevel(logging.INFO)

        self.register_thread('main')

        main = MainWindow(self)
        main.log_view.updater("CoralFTP %s\n" % coralftp_version)
        gtk.threads_enter()
        gtk.main()
        gtk.threads_leave()
        return

    def terminate(self):
        gtk.main_quit()
        logging.debug('main thread quit')

    def get_site_manager(self):
        if not self.__site_manager:
            self.__site_manager = SiteManager()
        return self.__site_manager

    def __getattr__(self, name):
        if name == 'config':
            return self.__config
        elif name == 'clipboard':
            if not self.__clipboard:
                display = gtk.gdk.display_get_default()
                self.__clipboard = Clipboard(display, "CLIPBOARD")
            return self.__clipboard
        else:
            raise AttributeError, name
        return

    def register_thread(self, name):
        id = thread.get_ident()
        if name == 'main':
            self.threads[id] = 'main thread'
            logging.debug('main thread(%d) registered' % id)
        elif name == 'ftp':
            self.threads[id] = 'ftp thread %d' % self.ftp_thread_count
            logging.debug('ftp thread %d(%d) registerd' \
                          % (id, self.ftp_thread_count))
            self.ftp_thread_count = self.ftp_thread_count + 1
        return

    def get_thread_name(self):
        id = thread.get_ident()
        if self.threads.has_key(id):
            return '%s(%d)' % (self.threads[id], id)
        else:
            return 'other thread(%d)' % id

class MainWindow:

    MODE_FTP = 1     # In this mode, one pane is local view another pane is
                     # remote view.
    MODE_LOCAL = 2   # In this mode, both pane are all local view
    MODE_FXP = 3     # In this mode, both pane are all remote view

    STATUSBAR_TYPE_GENERAL = 1
    STATUSBAR_TYPE_TRANSFER = 2

    __mode = None
    __statusbar = None
    __statusbar_type = None
    
    def __init__(self, coralftp):
        self.__xml = get_glade_xml('main_window')
        self.__xml.signal_autoconnect(self)
        self.widget = self.__xml.get_widget('main_window')
        self.vbox = self.__xml.get_widget('vbox')
        self.__statusbar = self.__xml.get_widget('status_bar')

        self.__coralftp = coralftp
        self.__config = self.__coralftp.config
        self.__setup_active_indicator()
        self.widget.connect("destroy",
                            lambda obj: self.__coralftp.terminate())
        self.create_contents()
        self.create_general_statusbar()
        self.widget.show_all()

        # setup start with site manager
        start_with_site_manager = config_value(
            self.__config, 'display', 'start_with_site_manager')
        if start_with_site_manager:
            gobject.idle_add(self.start_with_site_manager)
        return

    def __setup_active_indicator(self):
        def on_active_indicator_color_change(config, id, value, *args):
            gtk.rc_parse_string('''
                style "active_view_indicator" { bg[NORMAL] = "%s" } 
                widget "*.active_view_indicator" style "active_view_indicator"
            ''' % value.get_string())
            return
            
        self.__config.notify_add('display', 'active_side_indicator_color',
                                 on_active_indicator_color_change)
            
        # color of active side bar
        active_side_indicator_color = config_value(
            self.__config, 'display', 'active_side_indicator_color')
        gtk.rc_parse_string('''
            style "active_view_indicator" { bg[NORMAL] = "%s" }
            widget "*.active_view_indicator" style "active_view_indicator"
        ''' % active_side_indicator_color)
        return

    def create_contents(self):
        # create a log buffer
        self.__log_buffer = TextBuffer()
        self.__mode = self.MODE_FTP
        self.__left_view = LocalView(self)
        self.__right_view = RemoteView(self)
        self.__transfer_view = TransferView(self)
        self.__log_view = LogView(self)
        return

    def create_statusbar(self):
        if self.__statusbar != None:
            self.__statusbar.foreach(lambda obj, data: obj.destroy(), None)
        else:
            event_box = EventBox()
            self.vbox.pack_end(event_box, expand=False, fill=True)
            self.__statusbar = Table(1, 24, True)
            event_box.add_child(self.__statusbar)
        return

    def save_statusbar_height(self, *args):
        statusbar_window = self.__statusbar.get_parent_window()
        w, self.__statusbar_height = statusbar_window.get_size()
        return
        
    def create_general_statusbar(self):
        self.__statusbar_type = self.STATUSBAR_TYPE_GENERAL
        self.create_statusbar()
        ef = EXPAND | FILL
        self.status1 = Frame()
        self.status1.add(Label())
        self.__statusbar.attach(self.status1, 0, 12, 0, 1, ef, ef, 0, 0)
        self.status1 = self.status1.get_child()
        self.status1.set_alignment(0, 0.5)
        self.status1.show()
        self.status2 = Frame()
        self.status2.add(Label())
        self.__statusbar.attach(self.status2, 12, 23, 0, 1, ef, ef, 0, 0)
        self.status2 = self.status2.get_child()
        self.status2.set_alignment(0, 0.5)
        self.status2.show()
        self.status3 = Statusbar()
        self.__statusbar.attach(self.status3, 23, 24, 0, 1, ef, ef, 0, 0)
        self.__statusbar.foreach(lambda obj: obj.show())
        gobject.idle_add(self.save_statusbar_height)
        return self.__statusbar

    def create_transfer_statusbar(self):
        self.__statusbar_type = self.STATUSBAR_TYPE_TRANSFER
        self.create_statusbar()
        ef = EXPAND | FILL
        self.status1 = Frame()
        self.status1.add(Label()) # Filename
        self.__statusbar.attach(self.status1, 0, 12, 0, 1, ef, ef, 0, 0)
        self.status1 = self.status1.get_child()
        self.status1.show()
        self.status1.set_alignment(0, 0.5)
        self.status2 = ProgressBar()
        self.status2.set_size_request(-1, self.__statusbar_height)
        self.__statusbar.attach(self.status2, 12, 16, 0, 1, ef, ef, 0, 0)
        self.status3 = Frame()
        self.status3.add(Label())
        self.__statusbar.attach(self.status3, 16, 17, 0, 1, ef, ef, 0, 0)
        self.status3 = self.status3.get_child()
        self.status3.show()
        self.status4 = Frame()
        self.status4.add(Label()) # "Elapsed: 00:00"
        self.__statusbar.attach(self.status4, 17, 20, 0, 1, ef, ef, 0, 0)
        self.status4 = self.status4.get_child()
        self.status4.show()
        self.status5 = Frame()
        self.status5.add(Label()) # "Remaining: 00:00"
        self.__statusbar.attach(self.status5, 20, 23, 0, 1, ef, ef, 0, 0)
        self.status5 = self.status5.get_child()
        self.status5.show()
        self.status6 = Statusbar()
        self.__statusbar.attach(self.status6, 23, 24, 0, 1, ef, ef, 0, 0)

        self.__statusbar.foreach(lambda obj: obj.show())
        return self.__statusbar

    def set_statusbar_style(self, obj, data):
        if type(obj) == Frame:
            obj.set_shadow_type(SHADOW_IN)
            obj.set_border_width(0)
            obj.get_child().modify_font(data)
        if type(obj) == ProgressBar:
            obj.modify_font(data)

    def set_active_view(self, view):
        if view == self.__left_view:
            self.__right_view.set_active_view(False)
            self.__left_view.set_active_view(True)
        elif view == self.__right_view:
            self.__left_view.set_active_view(False)
            self.__right_view.set_active_view(True)

    def get_another_view(self, view):
        if view == self.__left_view:
            return self.__right_view
        elif view == self.__right_view:
            return self.__left_view
        else:
            raise ValueError

    def __getattr__(self, name):
        if name == 'transfer_view':
            return self.__transfer_view
        elif name == 'log_view':
            return self.__log_view
        elif name == 'log_buffer':
            return self.__log_buffer
        elif name == 'update_queue':
            return self.__update_queue
        elif name == 'mode':
            return self.__mode
        elif name == 'statusbar_type':
            return self.__statusbar_type
        elif name == 'right_view':
            return self.__right_view
        elif name == 'left_view':
            return self.__left_view
        elif name == 'remote_view':
            if self.__mode == MainWindow.MODE_FTP:
                if isinstance(self.__right_view, RemoteView):
                    return self.__right_view
                elif isinstance(self.__left_view, RemoteView):
                    return self.__left_view
        elif name == 'local_view':
            if self.__mode == MainWindow.MODE_FTP:
                if isinstance(self.__left_view, LocalView):
                    return self.__left_view
                elif isinstance(self.__right_view, LocalView):
                    return self.__right_view
        elif name == 'coralftp':
            return self.__coralftp
        elif name == 'config':
            return self.__config
        elif name == 'xml':
            return self.__xml
        else:
            raise AttributeError, name

    def start_with_site_manager(self):
        self.on_mmi_site_manager_activate(None)
        return

    def on_mmi_preferences_activate(self, obj, *args):
        d = ConfigurationDialog(self.__coralftp)
        resp = d.run()

    def on_mmi_site_manager_activate(self, obj, *args):
        d = SiteManagerDialog(self.__coralftp)
        resp = d.run()
        if resp == RESPONSE_OK:
            s = d.get_selected_site()
        d.destroy()
        if resp == RESPONSE_OK:
            self.remote_view.connect_site(s)
        d = None
        return

    def on_mmi_about_dialog_activate(self, *args):
        dlg = AboutDialog(self.__coralftp)
        dlg.run()
        return

    def on_mmi_quick_connect_activate(self, *args):
        self.__right_view.on_quick_connect_action_execute(*args)
        return

    def on_mmi_quit_activate(self, *args):
        self.widget.emit('destroy')
        return

    def on_mmi_system_env_activate(self, *args):
        def on_response(obj, resp, *args):
            def on_message_response(obj, resp, *args):
                obj.destroy()
                if resp == RESPONSE_YES:
                    self.widget.emit('destroy')
                return

            need_restart = obj.need_restart
            obj.destroy()
            if need_restart:
                dlg = MessageDialog(flags=DIALOG_MODAL,
                                    type=MESSAGE_QUESTION,
                                    buttons=BUTTONS_YES_NO,
                                    message_format=_("You have changed the configuration system CoralFTP use, and we suggest you restart CoralFTP now. Do you want to quit CoralFTP now?"))
                dlg.connect('response', on_message_response)
                dlg.show()
                dlg.set_modal(True)
            return

        from system_env import SystemEnvDialog
        dlg = SystemEnvDialog(self)
        dlg.connect('response', on_response)
        dlg.run()
        return

    def show_general_status1(self, message = ''):
        self.status1.set_text(message)

    def show_general_status2(self, message = ''):
        self.status2.set_text(message)

class AboutDialog:

    def __init__(self, parent):
        self.__xml = get_glade_xml('about_dialog')
        label_program_name = self.__xml.get_widget('program_name')
        label_program_name.set_text('CoralFTP %s' % coralftp_version)
        self.__xml.signal_autoconnect(self)

    def run(self):
        dlg = self.__xml.get_widget('about_dialog')
        resp = dlg.run()
        dlg.destroy()

    def on_btn_homepage_clicked(self, obj, *args):
        os.spawnlp(os.P_NOWAIT, "sensible-browser", "sensible-browser", \
                   "http://sourceforge.net/projects/coralftp")
        return
